// 让root作为模块进行导出
const { createRoot } = ReactDOM;
import PureComponent from './PureComponent';

export const root = createRoot(document.getElementById('app'));

const states = [];
const stateSetters = [];
const effectDepArrs = [];
const memoArr = [];
const callbackArr = [];

let stateIndex = 0;
let effectIndex = 0;
let memoIndex = 0;
let callbackIndex = 0;

export function useState(initialState){
  states[stateIndex] = createState(initialState, stateIndex);
  
  if(!stateSetters[stateIndex]){
    stateSetters.push(createStateSetter(stateIndex));
  }

  const _state = states[stateIndex];
  const _setState = stateSetters[stateIndex];

  stateIndex++;

  return [
    _state,
    _setState
  ]
}

export function useReducer(reducer, initialState){
  const [ state, setState ] = useState(initialState);

  function dispatch(actions){
    const newState = reducer(state, actions);
    setState(newState);
  }

  return [ state, dispatch ]
}

export function useEffect(cb, depArr){
  if(typeof cb !== 'function'){
    throw new TypeError('Parameter 1 must be a callback function.');
  }

  if(
    !Array.isArray(depArr) && 
    depArr === undefined
  ){
    throw new TypeError('Parameter 2 must be a dependencies array.');
  }

  /**
   * depArr.some() 检查只要有一个满足就会返回true，如果全部都不满足则返回false
   * effectDepArrs[effectIndex] 该数组元素不存在时则返回true，代表useEffect首次执行
   */
  const isChanged = effectDepArrs[effectIndex]
    ? depArr.some(
      (dep, index) => dep !== effectDepArrs[effectIndex][index]
    )
    : true;

  if(isChanged || depArr === undefined){
    isChanged && cb();
  }

  effectDepArrs[effectIndex] = depArr; 
  effectIndex++; 
}

export function memo(Fc){
  return class extends PureComponent{
    render(){
      return Fc(this.props);
    }
  }
}

export function useMemo(cb, depArr){
  if(memoArr[memoIndex]){
    const [ _memo, _depArr ] = memoArr[memoIndex];
    const isFullySame = depArr.every(
      (dep, index) => dep === _depArr[index]
    );
    //有缓存cb但没有更新时执行cb并返回之前的引用对象
    if(isFullySame){
      memoIndex ++;
      return _memo;
    }else{
      //有缓存cb但有更新时则执行拿到新的引用并返回出去
      return setNewMemo(cb, depArr);
    }
  }else{
    return setNewMemo(cb, depArr);
  }

  function setNewMemo(cb, depArr){
    //对应 useMemo(() => ({state})) 
    //回调执行就可以拿到返回的新引用
    const memo = cb();
    memoArr[memoIndex] = [ memo, depArr ];
    memoIndex ++;
    return memo;
  }
}

export function useCallback(cb, depArr){
  if(callbackArr[callbackIndex]){
    const [ _cb, _depArr ] = callbackArr[callbackIndex];
    const isFullySame = depArr.every(
      (dep, index) => dep === _depArr[index]
    );
    //有缓存cb但没有更新时返回之前的cb
    if(isFullySame){
      callbackIndex ++;
      return _cb;
    }else{
      //有缓存cb但有更新时则返回当前新的回调函数并缓存到`callbackArr`
      return setNewCallback(cb, depArr);
    }
  }else{
    return setNewCallback(cb, depArr);
  }

  function setNewCallback(cb, depArr){
    callbackArr[callbackIndex] = [ cb, depArr ];
    callbackIndex ++;
    return cb;
  }
}

function createState(initialState, stateIndex){
  return states[stateIndex] ? states[stateIndex] : initialState;
}

function createStateSetter(stateIndex){
  return (newState) => {
    if(typeof newState === 'function'){
      //如果是函数语句则让这函数执行拿到新的状态结果
      states[stateIndex] = newState(states[stateIndex]);
    }else{
      //否则 拿当前的状态
      states[stateIndex] = newState;
    }
    render();
  }
}

async function render(){
  //懒加载的方式对视图组件进行导入
  const App = (await import('./App')).default;
  // console.log(App);
  //ƒ App() {...}
  stateIndex = 0;
  effectIndex = 0;
  memoIndex = 0;
  root.render(<App />);
}

